package com.sc.nft.controller;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.sc.nft.annotation.UserLogin;
import com.sc.nft.controller.request.IdRequest;
import com.sc.nft.dao.BoxTakePoolDao;
import com.sc.nft.dao.DigitalCollectionProductInfoDao;
import com.sc.nft.dao.UserCollectionDao;
import com.sc.nft.entity.BoxTakePool;
import com.sc.nft.entity.DigitalCollectionProductInfo;
import com.sc.nft.entity.UserInfo;
import com.sc.nft.entity.dto.ConversionNftDTO;
import com.sc.nft.entity.dto.QueryBoxConditionDTO;
import com.sc.nft.entity.req.VoucherConversionNftRequest;
import com.sc.nft.entity.vo.*;
import com.sc.nft.enums.BoxTakePoolConvertScenarioEnum;
import com.sc.nft.exception.GlobalException;
import com.sc.nft.exception.GlobalRunTimeException;
import com.sc.nft.pool.UserPool;
import com.sc.nft.service.BoxTakeConversionConditionService;
import com.sc.nft.service.BoxTakePoolService;
import com.sc.nft.service.DigitalCollectionService;
import com.sc.nft.service.EquityPropsService;
import com.sc.nft.sup.ErrorCode;
import com.sc.nft.sup.PageResult;
import com.sc.nft.sup.Result;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.http.MediaType;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.validation.Valid;
import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

@Slf4j
@RequiredArgsConstructor
@RestController
@RequestMapping("/voucherConversion")
@Api(tags = "商品兑换NFT")
public class VoucherConversionNFTController {

    private final BoxTakePoolDao boxTakePoolDao;

    private final DigitalCollectionService digitalCollectionService;

    private final EquityPropsService equityPropsService;

    private final BoxTakePoolService boxTakePoolService;

    private final RedissonClient redissonClient;

    private final UserCollectionDao userCollectionDao;

    private final DigitalCollectionProductInfoDao digitalCollectionProductInfoDao;
    private final BoxTakeConversionConditionService boxTakeConversionConditionService;

    @PostMapping(value = "/nftList", produces = MediaType.APPLICATION_JSON_VALUE)
    @ApiOperation(value = "nft兑换列表", notes = "nft兑换列表", httpMethod = "POST", produces = MediaType.APPLICATION_JSON_VALUE)
    @UserLogin
    public PageResult<BoxPoolVO> nftList(@RequestBody @Valid VoucherConversionNftRequest request) {
        if (Objects.equals(request.getConvertScenario(), BoxTakePoolConvertScenarioEnum.DAO_CONVERT)
                && Objects.isNull(request.getDaoId())) {
            throw new GlobalRunTimeException("品牌不能为空");
        }
        Page<BoxPoolVO> boxPoolVOPage = boxTakePoolDao.voucherWebConversion(true, null, request.getConvertScenario(), request.getDaoId(), request.getPageNo(), request.getPageSize());
        boxPoolVOPage.getRecords().forEach(boxPoolVO -> {
            QueryBoxConditionDTO queryBoxConditionDTO = new QueryBoxConditionDTO();
            queryBoxConditionDTO.setBoxId(boxPoolVO.getId());
            List<QueryBoxConditionVO> queryBoxConditionVOS = boxTakeConversionConditionService.queryBoxCondition(queryBoxConditionDTO);
            QueryWebBoxConditionPackagingVO queryWebBoxConditionPackagingVO = new QueryWebBoxConditionPackagingVO();
            queryWebBoxConditionPackagingVO.setQueryBoxCondition(BeanUtil.copyToList(queryBoxConditionVOS, QueryWebBoxConditionVO.class));
            // 所需藏品的数据
            queryWebBoxConditionPackagingVO.nftConfiguration(null);
            boxPoolVO.setNftExclusive(BeanUtil.isNotEmpty(queryWebBoxConditionPackagingVO.getQueryBoxConditionCollection()) ? queryWebBoxConditionPackagingVO.getQueryBoxConditionCollection().getName() : null);
        });
        return PageResult.success(boxPoolVOPage);
    }

    @PostMapping(value = "/findById", produces = MediaType.APPLICATION_JSON_VALUE)
    @ApiOperation(value = "根据藏品id查询详情内容", notes = "根据藏品id查询详情内容", httpMethod = "POST", produces = MediaType.APPLICATION_JSON_VALUE)
    @UserLogin
    public Result<GetDetailsBasedOnTheCollectionIdVO> findById(@RequestBody @Validated IdRequest idRequest) {
        return Result.success(digitalCollectionService.getDetailsBasedOnTheCollectionId(idRequest.getId()));
    }

//    @PostMapping(value = "/findByIdGetCoinCertificate", produces = MediaType.APPLICATION_JSON_VALUE)
//    @ApiOperation(value = "根据兑换id获取用户当前拥有的兑换券", notes = "根据兑换id获取用户当前拥有的兑换券", httpMethod = "POST", produces = MediaType.APPLICATION_JSON_VALUE)
//    @UserLogin
//    public Result<GetCoinCertificateVO> findByIdGetCoinCertificate(@RequestBody @Validated IdRequest idRequest) {
//        return Result.success(equityPropsService.findByIdGetCoinCertificate(idRequest.getId(), UserPool.getUser()));
//    }

    @SneakyThrows
    @PostMapping(value = "/conversion", produces = MediaType.APPLICATION_JSON_VALUE)
    @ApiOperation(value = "兑换NFT", notes = "兑换NFT", httpMethod = "POST", produces = MediaType.APPLICATION_JSON_VALUE)
    @UserLogin
    public Result conversion(@RequestBody @Validated ConversionNftDTO conversionNftDTO) {
        UserInfo user = UserPool.getUser();
        // lock的key组成 conversion:商品id
        BoxTakePool boxTakePool = boxTakePoolDao.getById(conversionNftDTO.getId());
        Assert.isTrue(boxTakePool.getStatus(), () -> new GlobalRunTimeException(ErrorCode.PRODUCT_HAS_BEEN_DESCONTINUED));
        DigitalCollectionProductInfo digitalCollectionProductInfo = digitalCollectionProductInfoDao.getById(boxTakePool.getProductBoxId());
        if (BeanUtil.isEmpty(digitalCollectionProductInfo)) {
            return Result.fail(ErrorCode.MALL_PRODUCT_DOES_NOT_EXIST);
        }

        if (BeanUtil.isEmpty(user)) {
            return Result.fail("用户不存在");
        }
        // 验证用户是否已实名
        if (!user.getIsSm()) {
            return Result.fail("请先实名认证");
        }

        // 验证用户是否已设置支付密码
        if (StrUtil.isEmpty(user.getTransactionPassword())) {
            return Result.fail("请先设置支付密码");
        }

        // 支付密码是否正确
        if (!user.getTransactionPassword().equals(conversionNftDTO.getPaymentPassword())) {
            throw new GlobalRunTimeException("支付密码错误");
        }

        //判定时间是否在允许时间段内
        Date now = new Date();
        //设置的开始和结束时间不为空，且当前时间不在两个时间段内则无法购买，历史老商品未配置时间段的则为永久出售不做限制 区分已结束、未开始
        if (null != digitalCollectionProductInfo.getBeginTime() && null != digitalCollectionProductInfo.getEndTime()) {
            if (now.before(digitalCollectionProductInfo.getBeginTime())) {
                return Result.fail("兑换活动未开始");
            }
            if (now.after(digitalCollectionProductInfo.getEndTime())) {
                return Result.fail("兑换活动已结束");
            }
        }

        if (digitalCollectionProductInfo.getIsRegisterTime()) {
            Assert.isTrue(user.getCreateTime().after(digitalCollectionProductInfo.getRegisterBeginTime()), () -> new GlobalException("注册时间不符合"));
            Assert.isTrue(user.getCreateTime().before(digitalCollectionProductInfo.getRegisterEndTime()), () -> new GlobalException("注册时间不符合"));
        }

        String key = StrUtil.format("conversion:{}", digitalCollectionProductInfo.getId());
        RLock conversion = redissonClient.getLock(key);
        log.info("兑换NFT锁单Key:{}", conversion);
        try {
            boolean b = conversion.tryLock(30, TimeUnit.SECONDS);
            Boolean result = Boolean.FALSE;
            if (b) {
                //是否还有可兑换数量
                MyCollectionNowVO collectionNowVO = userCollectionDao.countMyCollectionNow(user.getId(), boxTakePool.getProductBoxId(), conversionNftDTO.getId());
                if (null != boxTakePool.getUpperLimit() && Objects.nonNull(collectionNowVO) && collectionNowVO.getCount()
                        >= boxTakePool.getUpperLimit()) {
                    return Result.fail("兑换已达上限");
                }
                result = boxTakePoolService.conversion(conversionNftDTO, user);
            }

            if (result) {
                return Result.success();
            } else {
                return Result.fail("兑换失败");
            }
        } finally {
            conversion.unlock();
        }
    }

}
